#include "ShMem.h"

#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>

#include <cstdlib>

namespace CExpert{

	ShMemException::ShMemException(const std::string& s)
		: std::runtime_error(s)
		, err_no (errno){
	}

  ShMem::ShMem(const char* name, OpenMode o_mode)
	  : name_(name){
      size_t slash_idx = 0;
      while ((slash_idx = name_.find("/", name_.empty()?0:1)) != std::string::npos){
          name_.replace(slash_idx, 1, "#");
      }
	  int mode = O_RDONLY;
	  if (o_mode == OMWRITE){
		  mode = O_RDWR | O_CREAT;
	  }
	  //create shared handle
	  shm_handle_ = shm_open(name_.c_str(), mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	  if (shm_handle_ < 0){
		  throw ShMemException(std::string("shm_open fails with name: ") + name_);
	  }
	  //set shared handle size
	  if (o_mode == OMWRITE && ftruncate(shm_handle_, length)){
		  shm_unlink(name_.c_str());
		  throw ShMemException("ftruncate fails");
	  }
	  //map shared handle to memory
	  buffer_ = mmap(NULL, length, PROT_READ | (o_mode == OMWRITE ? PROT_WRITE : 0), MAP_SHARED, shm_handle_, 0);
	  if (buffer_ == MAP_FAILED){
		  shm_unlink(name_.c_str());
		  throw ShMemException("mmap fails");
	  }
  }
  ShMem::~ShMem(){
	  if (buffer_ and buffer_ != MAP_FAILED)
		  munmap(buffer_, length);
	  //do not unlink object: see man shm_unlink(). minor memory leaks.
	  //shm_unlink(name_.c_str());
  }

	ShMem::byte_ptr ShMem::get_memory(){
		return reinterpret_cast<byte_ptr>(buffer_);
	}

	const ShMem::byte_ptr ShMem::get_read_memory() const{
		return reinterpret_cast<byte_ptr>(buffer_);
	}
}
